﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Syndication;
using System.Xml;
using System.Xml.Linq;

using GeoSyndication.Common;
using GeoSyndication.GeoRSS;

namespace GeoSyndication.GeoRSS
{
    public class GeoRSSFeedBuilder
    {
        #region Private Fields

        public static Dictionary<Type, Func<GeoRssItem, SyndicationItem>> Formatters = new Dictionary<Type, Func<GeoRssItem, SyndicationItem>>();
        
        #endregion

        #region Constructor
        
        static GeoRSSFeedBuilder()
        {
            Formatters.Add(typeof(GeoRssLineItem), i => GetLineItem((i as GeoRssLineItem)));
            Formatters.Add(typeof(GeoRssPointItem), i => GetPointItem((i as GeoRssPointItem)));
            Formatters.Add(typeof(GeoRssPolygonItem), i => GetPolygonItem((i as GeoRssPolygonItem)));
            Formatters.Add(typeof(GeoRssItem), i => GetItem((i as GeoRssItem)));
        } 

        #endregion

        #region Public Methods

        public static SyndicationFeed GetFeed(GeoRSSData builder)
        {
            if (builder == null)
            {
                throw new ArgumentException("You must provide GeoRSSdata");
            }

            SyndicationFeed feed = new SyndicationFeed(builder.Title, builder.Description, null);

            List<SyndicationItem> items = new List<SyndicationItem>();

            builder.Items.ForEach(item =>
                {
                    items.Add(Formatters[item.GetType()].Invoke(item));
                });

            feed.Items = items;

            return feed;
        } 

        #endregion

        #region Private Methods

        private static SyndicationItem GetItem(GeoRssItem pointItem)
        {
            SyndicationItem sy_item = BuildItem(pointItem.Title, pointItem.Description, pointItem.Icon, pointItem.Link, pointItem.Categories, pointItem.Date);
            return sy_item;
        }

        private static SyndicationItem GetPointItem(GeoRssPointItem pointItem)
        {
            SyndicationItem sy_item = BuildItem(pointItem.Title, pointItem.Description, pointItem.Icon, pointItem.Link, pointItem.Categories, pointItem.Date);
            XNamespace grns = "http://www.georss.org/georss";
            XElement x = new XElement(grns + "point", new XAttribute(XNamespace.Xmlns + "georss", grns.NamespaceName), pointItem.Point.Latitude.ToString() + " " + pointItem.Point.Longitude.ToString());
            SyndicationElementExtension sext = new SyndicationElementExtension(x);
            sy_item.ElementExtensions.Add(new SyndicationElementExtension(x));
            return sy_item;
        }

        private static SyndicationItem GetPolygonItem(GeoRssPolygonItem polygonItem)
        {
            SyndicationItem sy_item = BuildItem(polygonItem.Title, polygonItem.Description, polygonItem.Icon, polygonItem.Link, polygonItem.Categories, polygonItem.Date);
            XNamespace grns = "http://www.georss.org/georss";
            XElement x = new XElement(grns + "polygon", new XAttribute(XNamespace.Xmlns + "georss", grns.NamespaceName), GetSequenceOfPoints(polygonItem.Polygon));
            sy_item.ElementExtensions.Add(new SyndicationElementExtension(x));
            return sy_item;
        }

        private static SyndicationItem GetLineItem(GeoRssLineItem lineItem)
        {
            SyndicationItem sy_item = BuildItem(lineItem.Title, lineItem.Description, lineItem.Icon, lineItem.Link, lineItem.Categories, lineItem.Date);
            XNamespace grns = "http://www.georss.org/georss";
            XElement x = new XElement(grns + "line", new XAttribute(XNamespace.Xmlns + "georss", grns.NamespaceName), GetSequenceOfPoints(lineItem.Line));
            sy_item.ElementExtensions.Add(new SyndicationElementExtension(x));
            return sy_item;
        }

        private static SyndicationItem BuildItem(string title, string description, string icon, string link, string[] categories, DateTime date)
        {
            SyndicationItem sy_item = new SyndicationItem(title, description, Utility.CreateUri(link));
            sy_item.PublishDate = date;
            Utility.BindCategories(categories, sy_item);

            if (!string.IsNullOrEmpty(icon))
            {
                sy_item.ElementExtensions.Add(AddIcon(icon));
            }
            if (!string.IsNullOrEmpty(link))
            {
                sy_item.ElementExtensions.Add(AddLink(link));
            }

            return sy_item;
        }

        private static SyndicationElementExtension AddLink(string link)
        {
            XElement x = new XElement("link", link);
            //XElement x = new XElement("link", new XAttribute( "href", link ) );
            return new SyndicationElementExtension(x);
        }

        private static SyndicationElementExtension AddIcon(string icon)
        {
            XElement x = new XElement("icon", icon);
            return new SyndicationElementExtension(x);
        }

        private static string GetSequenceOfPoints(Point[] line)
        {
            StringBuilder xml = new StringBuilder();

            foreach (Point x in line)
            {
                xml.Append(x.ToString() + " ");
            }

            return xml.ToString();
        }

        #endregion
    }
}
